﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace JQuery.WebKit.Common.Extenstions
{
    public static class GenericExtensions
    {
        public static string ToStringExt(this object instance)
        {
            if (instance == null)
            {
                return string.Empty;
            }
            return instance.ToString();
        }

        //public static IList<T> DependencySort<T>(this IEnumerable<T> list, string uniqueIDProperty, string dependencyListProperty) where T : class
        //{
        //    if (list == null) { return null; }

        //    int count = list.Count();
        //    TopologicalSorter topSorter = new TopologicalSorter(count);
        //    Dictionary<string, int> _indexes = new Dictionary<string, int>();

        //    //add vertices
        //    for (int i = 0; i < count; i++)
        //    {
        //        _indexes[list.ElementAt(i).GetPropertyValue(uniqueIDProperty) as string] = topSorter.AddVertex(i);
        //    }

        //    //add edges
        //    for (int i = 0; i < count; i++)
        //    {
        //        IEnumerable<T> dependencyList = list.ElementAt(i).GetPropertyValue(dependencyListProperty) as IEnumerable<T>;
        //        if (dependencyList != null)
        //        {
        //            foreach (var edge in dependencyList)
        //            {
        //                topSorter.AddEdge(i, _indexes[edge.GetPropertyValue(uniqueIDProperty) as string]);
        //            }
        //        }
        //    }

        //    int[] sortOrder = topSorter.Sort();
        //    IList<T> dependencySortResult = new List<T>();
        //    for (int i = 0; i < sortOrder.Length; i++)
        //    {
        //        dependencySortResult.Add(list.ElementAt(sortOrder[i]));
        //    }
        //    return dependencySortResult;
        //}

        public static IList<T> DependencySort<T>(this IList list, string dependencyListProperty) where T : class
        {
            if (typeof(T).GetInterface("IDependencySort`1") == null)
            {
                throw new ApplicationException(string.Format("Type {0} must implemenet IDependencySort.", typeof(T).Name));
            }
            if (list == null || list.Count == 0) { return null; }

            List<IDependencySort<T>> internalList = list.Cast<IDependencySort<T>>().ToList();
            MarkVisitedRecursive<T>(internalList, false);
            List<IDependencySort<T>> lst = VisitTopLevel(internalList);
            return lst.Cast<T>().ToList() as IList<T>;
        }

        private static List<IDependencySort<T>> VisitTopLevel<T>(List<IDependencySort<T>> topLevelList) where T : class
        {
            List<IDependencySort<T>> list = new List<IDependencySort<T>>();
            foreach (var n in topLevelList)
            {
                Visit(n, list, n);
            }
            return list;
        }

        private static bool Visit<T>(IDependencySort<T> n, IList<IDependencySort<T>> list, IDependencySort<T> root) where T : class
        {
            if (n.Visited)
            {
                return n.Root != root;
            }
            n.Visited = true;
            n.Root = root;
            if (n.Dependencies != null)
            {
                foreach (IDependencySort<T> dependency in n.Dependencies)
                {
                    if (!Visit(dependency, list, root) && n != root)
                    {
                        throw new Exception("Cycle detected from " + n.ID + " to " + dependency.ID);
                    }
                }
            }
            if (!list.Contains(n))
            {
                list.Add(n);
            }
            return true;
        }

        private static void MarkVisitedRecursive<T>(IList<IDependencySort<T>> list, bool visited) where T : class
        {
            if (list == null)
            {
                return;
            }
            foreach (var item in list)
            {
                item.Visited = visited;
                if (item.Dependencies != null)
                {
                    MarkVisitedRecursive<T>(item.Dependencies, visited);
                }
            }
        }
    }
}
